/******************************************************************************************/

/********************************IT ALL HAVE TO BE HERE************************************/

/*****************************************SORRY********************************************/

#include "parserCalc.hpp"
#include <string>
#include <iostream>
#include <wchar.h>
//#include <vcclr.h>




// Forward declarations
void CalcBulk();

// Operator callback functions
value_type Mega(value_type a_fVal) { return a_fVal * 1e6; }
value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; }
value_type Rnd(value_type v) { return v*std::rand()/(value_type)(RAND_MAX+1.0); }
value_type Not(value_type v) { return v==0; }
value_type Add(value_type v1, value_type v2) { return v1+v2; }
value_type Mul(value_type v1, value_type v2) { return v1*v2; }

//---------------------------------------------------------------------------
value_type ThrowAnException(value_type)
{
  throw std::runtime_error("This function does throw an exception.");
}

//---------------------------------------------------------------------------
value_type BulkFun1(int nBulkIdx, int nThreadIdx, value_type v1)
{
  // Note: I'm just doing something with all three parameters to shut
  // compiler warnings up!
  return nBulkIdx + nThreadIdx + v1;
}

//---------------------------------------------------------------------------
value_type Ping()
{
  mu::console() << "ping\n";
  return 0;
}

//---------------------------------------------------------------------------
value_type StrFun0(const char_type *szMsg)
{
  if (szMsg)
    mu::console() << szMsg << std::endl;

  return 999;
}

//---------------------------------------------------------------------------
value_type StrFun2(const char_type *v1, value_type v2,value_type v3)
{
  mu::console() << v1 << std::endl;
  return v2+v3;
}

//---------------------------------------------------------------------------
value_type Debug(mu::value_type v1, mu::value_type v2)
{
  ParserBase::EnableDebugDump(v1!=0, v2!=0);
  mu::console() << _T("Bytecode dumping ") << ((v1!=0) ? _T("active") : _T("inactive")) << _T("\n");
  return 1;
}

//---------------------------------------------------------------------------
// Factory function for creating new parser variables
// This could as well be a function performing database queries.
value_type* AddVariable(const char_type *a_szName, void *a_pUserData)
{
  // I don't want dynamic allocation here, so i used this static buffer
  // If you want dynamic allocation you must allocate all variables dynamically
  // in order to delete them later on. Or you find other ways to keep track of
  // variables that have been created implicitely.
  static value_type afValBuf[100];
  static int iVal = 0;

  mu::console() << _T("Generating new variable \"")
                << a_szName << _T("\" (slots left: ")
                << 99-iVal << _T(")")
                << _T(" User data pointer is:")
                << std::hex << a_pUserData <<endl;

  afValBuf[iVal] = 0;
  if (iVal>=99)
    throw mu::ParserError( _T("Variable buffer overflow.") );

  return &afValBuf[iVal++];
}

int IsHexValue(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal)
{
  if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') )
    return 0;

  unsigned iVal(0);

  // New code based on streams for UNICODE compliance:
  stringstream_type::pos_type nPos(0);
  stringstream_type ss(a_szExpr + 2);
  ss >> std::hex >> iVal;
  nPos = ss.tellg();

  if (nPos==(stringstream_type::pos_type)0)
    return 1;

  *a_iPos += (int)(2 + nPos);
  *a_fVal = (value_type)iVal;
  return 1;
}

//---------------------------------------------------------------------------
value_type SelfTest()
{
  mu::console() << _T( "-----------------------------------------------------------\n");
  mu::console() << _T( "Running test suite:\n\n");

  // Skip the self test if the value type is set to an integer type.
  if (mu::TypeInfo<mu::value_type>::IsInteger())
  {
    mu::console() << _T( "  Test skipped: integer data type are not compatible with the unit test!\n\n");
  }
  else
  {
    mu::Test::ParserTester pt;
    pt.Run();
  }

  return 0;
}

//---------------------------------------------------------------------------
void ListVar(const mu::ParserBase &parser)
{
  // Query the used variables (must be done after calc)
  mu::varmap_type variables = parser.GetVar();
  if (!variables.size())
    return;

  cout << "\nParser variables:\n";
  cout <<   "-----------------\n";
  cout << "Number: " << (int)variables.size() << "\n";
  varmap_type::const_iterator item = variables.begin();
  for (; item!=variables.end(); ++item)
    mu::console() << _T("Name: ") << item->first << _T("   Address: [0x") << item->second << _T("]\n");
}

//---------------------------------------------------------------------------
void ListConst(const mu::ParserBase &parser)
{
  mu::console() << _T("\nParser constants:\n");
  mu::console() << _T("-----------------\n");

  mu::valmap_type cmap = parser.GetConst();
  if (!cmap.size())
  {
    mu::console() << _T("Expression does not contain constants\n");
  }
  else
  {
    valmap_type::const_iterator item = cmap.begin();
    for (; item!=cmap.end(); ++item)
      mu::console() << _T("  ") << item->first << _T(" =  ") << item->second << _T("\n");
  }
}

//---------------------------------------------------------------------------
void ListExprVar(const mu::ParserBase &parser)
{
  string_type sExpr = parser.GetExpr();
  if (sExpr.length()==0)
  {
    cout << _T("Expression string is empty\n");
    return;
  }

  // Query the used variables (must be done after calc)
  mu::console() << _T("\nExpression variables:\n");
  mu::console() <<   _T("---------------------\n");
  mu::console() << _T("Expression: ") << parser.GetExpr() << _T("\n");

  varmap_type variables = parser.GetUsedVar();
  if (!variables.size())
  {
    mu::console() << _T("Expression does not contain variables\n");
  }
  else
  {
    mu::console() << _T("Number: ") << (int)variables.size() << _T("\n");
    mu::varmap_type::const_iterator item = variables.begin();
    for (; item!=variables.end(); ++item)
      mu::console() << _T("Name: ") << item->first << _T("   Address: [0x") << item->second << _T("]\n");
  }
}

//---------------------------------------------------------------------------
/** \brief Check for external keywords.
*/
int CheckKeywords(const mu::char_type *a_szLine, mu::Parser &a_Parser)
{
  string_type sLine(a_szLine);

  if ( sLine == _T("quit") )
  {
    return -1;
  }
  else if ( sLine == _T("list var") )
  {
    ListVar(a_Parser);
    return 1;
  }
  else if ( sLine == _T("opt on") )
  {
    a_Parser.EnableOptimizer(true);
    mu::console() << _T("Optimizer enabled\n");
    return 1;
  }
  else if ( sLine == _T("opt off") )
  {
    a_Parser.EnableOptimizer(false);
    mu::console() << _T("Optimizer disabled\n");
    return 1;
  }
  else if ( sLine == _T("list const") )
  {
    ListConst(a_Parser);
    return 1;
  }
  else if ( sLine == _T("list exprvar") )
  {
    ListExprVar(a_Parser);
    return 1;
  }
  else if ( sLine == _T("list const") )
  {
    ListConst(a_Parser);
    return 1;
  }
  else if ( sLine == _T("locale de") )
  {
    mu::console() << _T("Setting german locale: ArgSep=';' DecSep=',' ThousandsSep='.'\n");
    a_Parser.SetArgSep(';');
    a_Parser.SetDecSep(',');
    a_Parser.SetThousandsSep('.');
    return 1;
  }
  else if ( sLine == _T("locale en") )
  {
    mu::console() << _T("Setting english locale: ArgSep=',' DecSep='.' ThousandsSep=''\n");
    a_Parser.SetArgSep(',');
    a_Parser.SetDecSep('.');
    a_Parser.SetThousandsSep();
    return 1;
  }
  else if ( sLine == _T("locale reset") )
  {
    mu::console() << _T("Resetting locale\n");
    a_Parser.ResetLocale();
    return 1;
  }
  else if ( sLine == _T("test bulk") )
  {
    mu::console() << _T("Testing bulk mode\n");
    CalcBulk();
    return 1;
  }

  return 0;
}

//---------------------------------------------------------------------------
void CalcBulk()
{
  const int nBulkSize = 200;
  value_type *x = new value_type[nBulkSize];
  value_type *y = new value_type[nBulkSize];
  value_type *result = new value_type[nBulkSize];

  try
  {
    for (int i=0; i<nBulkSize; ++i)
    {
      x[i] = i;
      y[i] = (value_type)i/10;
    }
    mu::Parser  parser;
    parser.DefineVar(_T("x"), x);
    parser.DefineVar(_T("y"), y);
    parser.DefineFun(_T("fun1"), BulkFun1);
    parser.SetExpr(_T("fun1(0)+x+y"));
    parser.Eval(result, nBulkSize);

    for (int i=0; i<nBulkSize; ++i)
    {
      mu::console() << _T("Eqn. ") << i << _T(": x=") << x[i] << _T("; y=") << y[i] << _T("; result=") << result[i] << _T("\n");
    }
  }
  catch(...)
  {
    delete [] x;
    delete [] y;
    delete [] result;
    throw;
  }

  delete [] x;
  delete [] y;
  delete [] result;
}

parserCalc::parserCalc(){

    // Add some variables
    value_type  vVarVal[] = { 1, 2 }; // Values of the parser variables
    parser.DefineVar(_T("a"), &vVarVal[0]);  // Assign Variable names and bind them to the C++ variables
    parser.DefineVar(_T("b"), &vVarVal[1]);
    parser.DefineStrConst(_T("strBuf"), _T("hello world") );
    parser.AddValIdent(IsHexValue);

    // Add user defined unary operators
    parser.DefinePostfixOprt(_T("M"), Mega);
    parser.DefinePostfixOprt(_T("m"), Milli);
    parser.DefineInfixOprt(_T("!"), Not);
    parser.DefineFun(_T("strfun0"), StrFun0);
    parser.DefineFun(_T("strfun2"), StrFun2);
    parser.DefineFun(_T("ping"), Ping);
    parser.DefineFun(_T("rnd"), Rnd);     // Add an unoptimizeable function
    parser.DefineFun(_T("throw"), ThrowAnException);

    parser.DefineOprt(_T("add"), Add, 0);
    parser.DefineOprt(_T("mul"), Mul, 1);

    // These are service and debug functions
    parser.DefineFun(_T("debug"), Debug);
    parser.DefineFun(_T("selftest"), SelfTest);
    //parser.DefineFun(_T("help"), Help);

    // Define the variable factory
    parser.SetVarFactory(AddVariable, &parser);

}
/****************************************************************************
* LEAVE COMMENTS -> FOR GUI
****************************************************************************/
void parserCalc::operator()(Specimen &argument)
{
    unsigned dim = argument.getDimensions();
    //cerr<<"DIM: "<<dim<<"\n";

    double fVal[5]={0};
    const string x="x";
    const string y="y";
    const string z="z";
    const string t="t";
    const string w="w";
    const char *nameTab[10]= {&x[0],&y[0],&z[0],&t[0],&w[0]};
    //const char *nameWsk;
    //nameWsk= &nameTab[0];
    for(int i=0;i<dim;++i){
        fVal[i] = (double) argument[i];
        parser.DefineVar(nameTab[i], &fVal[i]);
        //cerr<<"\n"<<nameTab[i]<<" -> "<<fVal[i]<<"\n";
    }
    argument.setFitness(evalExpression());
    cerr<<"argument fitness set to: "<< evalExpression()<<"\n";
   // cerr
}

void parserCalc::writeExpression(string_type expression){

    // string_type sLine;
    // std::cout << "Please, give me expression\n";
    // std::getline(mu::console_in(), sLine);
    parser.SetExpr(expression);
    tmpFunction=expression;
    // comment this
    mu::console() << _T("Function is set to: ") << expression <<_T("\n");
    //mu::console() << std::setprecision(12);
}

float parserCalc::evalExpression(){
    mu::console() << _T("Evaluate: ") << tmpFunction <<_T("\n");
    parser.SetExpr(tmpFunction);
    mu::console() << std::setprecision(12);
    //mu::console() << _T("ans=") << parser.Eval() << _T("\n");
    return parser.Eval();
}
/*void parserCalc::setVariables(int howMany){
    string_type sLine;

    for(int i = 0; i < howMany; ++i){
        std::getline(mu::console_in(), sLine);
        parser.SetExpr(sLine);
        mu::console() << std::setprecision(12);
        mu::console() << _T("ans=") << parser.Eval() << _T("\n");
    }
}*/

/*void parserCalc::setVariables(const long double korosu){
    double fVal = (double) korosu;
    const char name = 'c';
    parser.DefineVar(&name, &fVal);
    parser.Eval();
    mu::console() << _T("Val eval was: ") << fVal <<_T("\n");
}*/

Gene variableCalc::operator()(const Gene &value)
{
    //dim=1;
    double fVal = (double) value;
    parser.DefineVar("x", &fVal);
    return evalExpression();
}

void variableCalc::writeExpression(string_type expression){
    // string_type sLine;
    // std::cout << "Please, give me expression\n";
    // std::getline(mu::console_in(), sLine);
    parser.SetExpr(expression);
    tmpFunction=expression;
    // comment this
    //mu::console() << _T("Function is set to: ") << expression <<_T("\n");
    //mu::console() << std::setprecision(12);
}

float variableCalc::evalExpression(){
    //mu::console() << _T("Evaluate: ") << tmpFunction <<_T("\n");
    parser.SetExpr(tmpFunction);
    //mu::console() << std::setprecision(12);
    //mu::console() << _T("ans=") << parser.Eval() << _T("\n");
    return parser.Eval();
}
